package com.zndroid.timePicker.impl;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

import com.zndroid.timePicker.R;
import com.zndroid.timePicker.widget.PickerView;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import static android.view.View.VISIBLE;

public class PickerDialog extends DialogFragment implements PickerView.OnSelectListener, View.OnClickListener {
    private static final String TAG = PickerDialog.class.getSimpleName();
    private static final int SPACE = 5;//可见条目数

    private Context mContext;

    private PickerView mpvDialogDay;
    private PickerView mpvDialogYear;
    private PickerView mpvDialogMonth;
    private PickerView mpvDialogHour;
    private PickerView mpvDialogMinute;
    private PickerView mpvDialogSecond;

    private TextView tvDialogTitle;
    private TextView tvDialogTopCancel;
    private TextView tvDialogTopConfirm;
    private TextView tvDialogBottomCancel;
    private TextView tvDialogBottomConfirm;
    private LinearLayout llDialogBottom;
    private LinearLayout llDialog;

    private int mCurrentYear;
    private int mCurrentMonth;
    private int mCurrentDay;
    private int mCurrentHour;
    private int mCurrentMinute;
    private int mCurrentSecond;

    private String mTitle;
    private int mGravity;
    private boolean isCanceledTouchOutside;
    private boolean isTwelveHour;
    private float mConfirmTextSize;
    private float mCancelTextSize;
    private int mConfirmTextColor;
    private int mCancelTextColor;
    private int mPickerType;
    private List<String> mYearList;
    private OnDateResultListener mOnDateResultListener;

    private PickerDialog(@NonNull Context context) {
        mContext = context;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.dialog_date_picker, container, false);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        initView(view);
        initData();

        updateLayout();
        updateView();
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        setStyle(DialogFragment.STYLE_NO_FRAME, R.style.PickerDialogTheme);
        super.onCreate(savedInstanceState);
    }

    private void initView(View view) {
        mpvDialogDay = view.findViewById(R.id.mpvDialogDay);
        mpvDialogYear = view.findViewById(R.id.mpvDialogYear);
        mpvDialogMonth = view.findViewById(R.id.mpvDialogMonth);
        mpvDialogHour = view.findViewById(R.id.mpvDialogHour);
        mpvDialogMinute = view.findViewById(R.id.mpvDialogMinute);
        mpvDialogSecond = view.findViewById(R.id.mpvDialogSecond);
        tvDialogTitle = view.findViewById(R.id.tvDialogTitle);

        tvDialogTopCancel = view.findViewById(R.id.tvDialogTopCancel);
        tvDialogTopConfirm = view.findViewById(R.id.tvDialogTopConfirm);
        tvDialogBottomCancel = view.findViewById(R.id.tvDialogBottomCancel);
        tvDialogBottomConfirm = view.findViewById(R.id.tvDialogBottomConfirm);
        llDialogBottom = view.findViewById(R.id.llDialogBottom);
        llDialog = view.findViewById(R.id.llDialog);

        mpvDialogYear.setOnSelectListener(this);
        mpvDialogMonth.setOnSelectListener(this);
        mpvDialogDay.setOnSelectListener(this);
        mpvDialogHour.setOnSelectListener(this);
        mpvDialogMinute.setOnSelectListener(this);
        mpvDialogSecond.setOnSelectListener(this);

        tvDialogTopCancel.setOnClickListener(this);
        tvDialogTopConfirm.setOnClickListener(this);
        tvDialogBottomCancel.setOnClickListener(this);
        tvDialogBottomConfirm.setOnClickListener(this);
    }

    private void initData() {
        List<String> mDataYear = new ArrayList<>();
        List<String> mDataMonth = new ArrayList<>();
        List<String> mDataHour = new ArrayList<>();
        List<String> mDataMinute = new ArrayList<>();
        List<String> mDataSecond = new ArrayList<>();

        mpvDialogYear.setText(mContext.getString(R.string.strDateYear));
        mpvDialogMonth.setText(mContext.getString(R.string.strDateMonth));
        mpvDialogDay.setText(mContext.getString(R.string.strDateDay));
        mpvDialogHour.setText(mContext.getString(R.string.strDateHour));
        mpvDialogMinute.setText(mContext.getString(R.string.strDateMinute));
        mpvDialogSecond.setText(mContext.getString(R.string.strDateSecond));

        //Year
        Calendar mCalendar = Calendar.getInstance();
        if (mYearList == null || mYearList.size() == 0) {
            int mMaxYear = mCalendar.get(Calendar.YEAR) + SPACE;
            int mMinYear = mCalendar.get(Calendar.YEAR) - SPACE;

            for (int i = mMinYear; i <= mMaxYear; i++) {
                mDataYear.add(String.valueOf(i));
            }
            mCurrentYear = mCalendar.get(Calendar.YEAR);
            mpvDialogYear.setData(mDataYear);
        } else {
            if (mYearList.contains(String.valueOf(mCalendar.get(Calendar.YEAR))))
                mCurrentYear = mCalendar.get(Calendar.YEAR);
            else
                mCurrentYear = Integer.parseInt(mYearList.get((int)(mYearList.size() / 2)));
            mpvDialogYear.setData(mYearList);
            mpvDialogYear.setDefaultValue(String.valueOf(mCurrentYear));
        }

        //Month+
        for (int i = 1; i < 13; i++) {
            if (i < 10) {
                mDataMonth.add("0" + i);
            } else {
                mDataMonth.add(String.valueOf(i));
            }
        }
        mpvDialogMonth.setData(mDataMonth);
        mCurrentMonth = mCalendar.get(Calendar.MONTH) + 1;
        mpvDialogMonth.setDefaultValue(String.valueOf(mCurrentMonth), DateType.MONTH, "-1");

        //Day
        mCurrentDay = mCalendar.get(Calendar.DAY_OF_MONTH);
        updateDay(mCurrentYear, mCurrentMonth);

        //Hour
        if (isTwelveHour) {
            mCurrentHour = mCalendar.get(Calendar.HOUR);
            addTimeData(mDataHour, 13, 12);
            mpvDialogHour.setData(mDataHour);
            mpvDialogHour.setDefaultValue(String.valueOf(mCurrentHour), DateType.HOUR_12, "12");
        } else {
            mCurrentHour = mCalendar.get(Calendar.HOUR_OF_DAY);
            addTimeData(mDataHour, 25, 24);
            mpvDialogHour.setData(mDataHour);
            mpvDialogHour.setDefaultValue(String.valueOf(mCurrentHour), DateType.HOUR_12, "24");
        }

        //Minute
        addTimeData(mDataMinute, 61, 60);
        mpvDialogMinute.setData(mDataMinute);
        mCurrentMinute = mCalendar.get(Calendar.MINUTE);
        mpvDialogMinute.setDefaultValue(String.valueOf(mCurrentMinute), DateType.MINUTE, "60");

        //Second
        addTimeData(mDataSecond, 61, 60);
        mpvDialogSecond.setData(mDataSecond);
        mCurrentSecond = mCalendar.get(Calendar.SECOND);
        mpvDialogSecond.setDefaultValue(String.valueOf(mCurrentSecond), DateType.SECOND, "60");
    }

    private void updateLayout() {
        Window window = getDialog().getWindow();

        if (window == null) return;

        WindowManager.LayoutParams params = window.getAttributes();
        params.gravity = mGravity;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        window.setBackgroundDrawableResource(android.R.color.transparent);

        getDialog().setCanceledOnTouchOutside(isCanceledTouchOutside);

        if (mGravity == Gravity.BOTTOM) {
            params.width = WindowManager.LayoutParams.MATCH_PARENT;
            llDialogBottom.setVisibility(View.GONE);
            llDialog.setBackgroundResource(R.drawable.dialog_date_picker_bottom_bg);
        } else {
            tvDialogTopCancel.setVisibility(View.GONE);
            tvDialogTopConfirm.setVisibility(View.GONE);
            llDialog.setBackgroundResource(R.drawable.dialog_date_picker_center_bg);
        }

        window.setAttributes(params);
    }

    private void setWeight(boolean Y, boolean M, boolean D, boolean HH, boolean MM, boolean SS) {
        if (Y) {
            mpvDialogYear.setVisibility(VISIBLE);
            mpvDialogYear.setLayoutParams(new LinearLayout.LayoutParams(0, dpToPx(mContext, 160), 1.0f));
        } else {
            mpvDialogYear.setVisibility(View.GONE);
        }
        if (M) {
            mpvDialogMonth.setVisibility(VISIBLE);
            mpvDialogMonth.setLayoutParams(new LinearLayout.LayoutParams(0, dpToPx(mContext, 160), 1.0f));
        } else {
            mpvDialogMonth.setVisibility(View.GONE);
        }
        if (D) {
            mpvDialogDay.setVisibility(VISIBLE);
            mpvDialogDay.setLayoutParams(new LinearLayout.LayoutParams(0, dpToPx(mContext, 160), 1.0f));
        } else {
            mpvDialogDay.setVisibility(View.GONE);
        }
        if (HH) {
            mpvDialogHour.setVisibility(VISIBLE);
            mpvDialogHour.setLayoutParams(new LinearLayout.LayoutParams(0, dpToPx(mContext, 160), 1.0f));
        } else {
            mpvDialogHour.setVisibility(View.GONE);
        }
        if (MM) {
            mpvDialogMinute.setVisibility(VISIBLE);
            mpvDialogMinute.setLayoutParams(new LinearLayout.LayoutParams(0, dpToPx(mContext, 160), 1.0f));
        } else {
            mpvDialogMinute.setVisibility(View.GONE);
        }
        if (SS) {
            mpvDialogSecond.setVisibility(VISIBLE);
            mpvDialogSecond.setLayoutParams(new LinearLayout.LayoutParams(0, dpToPx(mContext, 160), 1.0f));
        } else {
            mpvDialogSecond.setVisibility(View.GONE);
        }

    }

    private void updateView() {
        //Setting
        if (!TextUtils.isEmpty(mTitle)) tvDialogTitle.setText(mTitle);

        switch (mPickerType) {
            case PickerType.ALL:
                setWeight(true, true, true, true, true, true);
                break;
            case PickerType.COMMON:
                setWeight(true, true, true, true, true, false);
                break;
            case PickerType.YMD:
                setWeight(true, true, true, false, false, false);
                break;
            case PickerType.MD:
                setWeight(false, true, true, false, false, false);
                break;
            case PickerType.HMS:
                setWeight(false, false, false, true, true, true);
                break;
            case PickerType.HM:
                setWeight(false, false, false, true, true, false);
                break;

        }

        if (mConfirmTextSize != 0.0f && mConfirmTextSize != -1.0f) {
            tvDialogTopConfirm.setTextSize(mConfirmTextSize);
            tvDialogBottomConfirm.setTextSize(mConfirmTextSize);
        }

        if (mConfirmTextColor != 0 && mConfirmTextColor != -1) {
            tvDialogTopConfirm.setTextColor(mConfirmTextColor);
            tvDialogBottomConfirm.setTextColor(mConfirmTextColor);
        }

        if (mCancelTextSize != 0.0f && mCancelTextSize != -1.0f) {
            tvDialogTopCancel.setTextSize(mCancelTextSize);
            tvDialogBottomCancel.setTextSize(mCancelTextSize);
        }

        if (mCancelTextColor != 0 && mCancelTextColor != -1) {
            tvDialogTopCancel.setTextColor(mCancelTextColor);
            tvDialogBottomCancel.setTextColor(mCancelTextColor);
        }
    }


    @Override
    public void onSelect(View v, String data) {
        int id = v.getId();
        if (id == R.id.mpvDialogYear) {
            mCurrentYear = Integer.parseInt(data);
            updateDay(mCurrentYear, mCurrentMonth);
        } else if (id == R.id.mpvDialogMonth) {
            mCurrentMonth = Integer.parseInt(data);
            updateDay(mCurrentYear, mCurrentMonth);
        } else if (id == R.id.mpvDialogDay) {
            mCurrentDay = Integer.parseInt(data);
        } else if (id == R.id.mpvDialogHour) {
            mCurrentHour = Integer.parseInt(data);
        } else if (id == R.id.mpvDialogMinute) {
            mCurrentMinute = Integer.parseInt(data);
        } else if (id == R.id.mpvDialogSecond) {
            mCurrentSecond = Integer.parseInt(data);
        }
    }

    @Override
    public void onClick(View v) {
        int i = v.getId();
        if (i == R.id.tvDialogTopCancel || i == R.id.tvDialogBottomCancel) {
            dismiss();

        } else if (i == R.id.tvDialogTopConfirm || i == R.id.tvDialogBottomConfirm) {
            if (mOnDateResultListener != null) {
                onResult();
                dismiss();
            }
        }
    }

    private void onResult() {
        long timeMillis;
        SimpleDateFormat dateFormat = (SimpleDateFormat) SimpleDateFormat.getDateInstance();

        switch (mPickerType) {
            case PickerType.ALL:
                timeMillis = getDateMills(mCurrentYear, mCurrentMonth, mCurrentDay, mCurrentHour, mCurrentMinute, mCurrentSecond);
                dateFormat.applyPattern(FormatType.Y_M_D_H_M_S);

                mOnDateResultListener.onDateResult(timeMillis);
                mOnDateResultListener.onDateResult(dateFormat.format(new Date(timeMillis)));
                break;
            case PickerType.HMS:
                timeMillis = getDateMills(mCurrentYear, mCurrentMonth, mCurrentDay, mCurrentHour, mCurrentMinute, mCurrentSecond);
                dateFormat.applyPattern(FormatType.H_M_S);

                mOnDateResultListener.onDateResult(timeMillis);
                mOnDateResultListener.onDateResult(dateFormat.format(new Date(timeMillis)));
                break;
            case PickerType.COMMON:
                timeMillis = getDateMills(mCurrentYear, mCurrentMonth, mCurrentDay, mCurrentHour, mCurrentMinute, 0);
                dateFormat.applyPattern(FormatType.Y_M_D_H_M);

                mOnDateResultListener.onDateResult(timeMillis);
                mOnDateResultListener.onDateResult(dateFormat.format(new Date(timeMillis)));
                break;
            case PickerType.HM:
                timeMillis = getDateMills(mCurrentYear, mCurrentMonth, mCurrentDay, mCurrentHour, mCurrentMinute, 0);
                dateFormat.applyPattern(FormatType.H_M);

                mOnDateResultListener.onDateResult(timeMillis);
                mOnDateResultListener.onDateResult(dateFormat.format(new Date(timeMillis)));
                break;
            case PickerType.YMD:
                timeMillis = getDateMills(mCurrentYear, mCurrentMonth, mCurrentDay, 0, 0,  0);
                dateFormat.applyPattern(FormatType.Y_M_D);

                mOnDateResultListener.onDateResult(timeMillis);
                mOnDateResultListener.onDateResult(dateFormat.format(new Date(timeMillis)));

                break;
            case PickerType.MD:
                timeMillis = getDateMills(mCurrentYear, mCurrentMonth, mCurrentDay, 0, 0,  0);
                dateFormat.applyPattern(FormatType.M_D);

                mOnDateResultListener.onDateResult(timeMillis);
                mOnDateResultListener.onDateResult(dateFormat.format(new Date(timeMillis)));
                break;
        }

    }

    public void setOnDateResultListener(OnDateResultListener onDateResultListener) {
        this.mOnDateResultListener = onDateResultListener;
    }

    private int getDayByYearMonth(int year, int month) {
        Calendar mCalendar = Calendar.getInstance();
        mCalendar.set(Calendar.YEAR, year);
        mCalendar.set(Calendar.MONTH, month - 1);
        return mCalendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    private void updateDay(int year, int month) {
        List<String> mDataDay = new ArrayList<>();
        int daySize = getDayByYearMonth(year, month);
        addTimeData(mDataDay, daySize + 1, 32);
        mpvDialogDay.setData(mDataDay);
        mpvDialogDay.setDefaultValue(String.valueOf(mCurrentDay), DateType.DAY, "-1");
    }

    private void addTimeData(List<String> list, int size, int equal) {
        for (int i = 1; i < size; i++) {
            if (i < 10) {
                list.add("0" + i);
            } else if (i == equal) {
                list.add("00");
            } else {
                list.add(String.valueOf(i));
            }
        }
    }

    private long getDateMills(int year, int month, int day, int hour, int minute, int second) {
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(year, month - 1, day, hour, minute, second);
        return calendar.getTimeInMillis();
    }

    public static class Builder {
        private Context mContext;
        private String mTitle;
        private int mGravity;
        private boolean isCanceledTouchOutside;
        private boolean isTwelveHour;
        private float mConfirmTextSize;
        private float mCancelTextSize;
        private int mConfirmTextColor;
        private int mCancelTextColor;
        private int mPickerType = PickerType.COMMON;
        private List<String> mYearList;
        private OnDateResultListener mOnDateResultListener;

        public Builder(Context mContext) {
            this.mContext = mContext;
        }

        public Builder setTitle(String mTitle) {
            this.mTitle = mTitle;
            return this;
        }

        public Builder setGravity(int mGravity) {
            this.mGravity = mGravity;
            return this;
        }

        public Builder setCanceledTouchOutside(boolean canceledTouchOutside) {
            isCanceledTouchOutside = canceledTouchOutside;
            return this;
        }

        public Builder setPickType(@PickerType int pickerType) {
            mPickerType = pickerType;
            return this;
        }

        /**
         * @param twelveHour 默认24小时制
         *                   12小时制时，上午、下午 {@link OnDateResultListener#onDateResult(long timeMillis)} 回调是准确的，
         *                   {@link OnDateResultListener#onDateResult(String formatDate)}回调仅显示是正确的，无 上午/下午标识
         * */
        public Builder set12HourModel(boolean twelveHour) {
            isTwelveHour = twelveHour;
            return this;
        }

        /**
         * set range of Year
         * */
        public Builder setYearRang(int min, int max) {
            if (min <=0 || max <= 0) return this;//默认不设置
            mYearList = new ArrayList<>();
            for (int i = Math.min(min, max); i <= Math.max(min, max); i ++) {
                mYearList.add(String.valueOf(i));
            }
            return this;
        }

        public Builder setConfirmStatus(float textSize, int textColor) {
            this.mConfirmTextSize = textSize;
            this.mConfirmTextColor = textColor;
            return this;
        }

        public Builder setCancelStatus(float textSize, int textColor) {
            this.mCancelTextSize = textSize;
            this.mCancelTextColor = textColor;
            return this;
        }

        public Builder setOnDateResultListener(OnDateResultListener onDateResultListener) {
            this.mOnDateResultListener = onDateResultListener;
            return this;
        }

        private void applyConfig(PickerDialog dialog) {
            if (this.mGravity == 0) this.mGravity = Gravity.CENTER;
            dialog.mContext = this.mContext;
            dialog.mTitle = this.mTitle;
            dialog.mGravity = this.mGravity;
            dialog.mPickerType = this.mPickerType;
            dialog.isTwelveHour = this.isTwelveHour;
            dialog.mConfirmTextSize = this.mConfirmTextSize;
            dialog.mConfirmTextColor = this.mConfirmTextColor;
            dialog.mCancelTextSize = this.mCancelTextSize;
            dialog.mCancelTextColor = this.mCancelTextColor;
            dialog.mYearList = this.mYearList;
            dialog.isCanceledTouchOutside = this.isCanceledTouchOutside;
            dialog.mOnDateResultListener = this.mOnDateResultListener;
        }

        public PickerDialog build() {
            PickerDialog dialog = new PickerDialog(mContext);
            applyConfig(dialog);
            return dialog;
        }
    }

    public interface OnDateResultListener {
        void onDateResult(long timeMillis);
        /** @param formatDate consistent with {@link FormatType}*/
        void onDateResult(String formatDate);
    }

    private float getScreenWidth(Activity context) {
        DisplayMetrics dm = new DisplayMetrics();
        context.getWindowManager().getDefaultDisplay().getMetrics(dm);
        return dm.widthPixels;
    }

    /**
     * dp to pix
     */
    private int dpToPx(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}
